<?php
#### FORM  #####################################################################
class Form {

	var $group;
	var $return_item_output;
	var $required;
	var $open_group;
	var $open_fieldset;
	var $validation;
	var $open_slider;
	var $center;

	#### OPEN FORM #############################################################
	function open($action = NULL, $id = 'post', $method = 'post', $type = FALSE) {
		Base::getThis();

		// Set default action
		if (!$action) $action = url('all');

		// Store form_id
		$this->form_id = $id;

		// Include validation JS
		$output = '<script language="javascript" src="media/javascript/validation.js" type="text/javascript"></script>';

		$output .= '<br /><form id="'.$id.'" method="'.$method.'" action="'.$action.'" enctype="multipart/form-data"'.$type.'>
		<input type="hidden" name="submit" value="true" />';

		if ($this->return_item_output) return $output;
		$this->output = $output;
	}

	#### BUILD NAME ############################################################
	function buildName($name, $id = FALSE) {

		// Check if our field is required, denoted by *
		if (strstr($name, '*')) {
			$name = str_replace('*', '', $name).' <span class="required">*</span>';
			$this->required = TRUE;
			if ($id) $this->validation[$id]['required'] = TRUE;
		}

		// Check if this field has a description
		$description = (isset($this->description[$id]) ? '<br /><div class="dt description">'.$this->description[$id].'</div>' : NULL);

		// Add feedback div for validation
		$feedback = ($id ? '<div id="feedback_'.$id.'" class="form feedback"></div>' : NULL);

	 	return '<dt><label for="'.str_replace(' ', '', $id).'">'.$name.'</label>'.$description.$feedback.'</dt>';
	}

	#### LABEL #################################################################
	function label($name = NULL) {
		$output = $this->buildName($name);

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### DESCRIPTION ###########################################################
	function description($id, $text = NULL, $output = FALSE) {

		// Set some variables to be used by input generating funcitons
		if (!$output) $this->description[$id] = $text;
		else $output = '<div class="dt_description">'.$text.'</div>';

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### OPEN FIELDSET #########################################################
	function openFieldset($legend = NULL) {
		if ($legend) $legend = '<legend>'.$legend.'</legend>';
		$output = '<fieldset>'.$legend;

		$this->open_fieldset = TRUE;

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### CLOSE FIELDSET ########################################################
	function closeFieldset() {
		$output = '</fieldset>';

		$this->open_fieldset = FALSE;

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### OPEN SLIDER ###########################################################
	function openSlider($name) {
		$output = slider('#'.sef($name).'-slider', '#'.sef($name).'-toggle');
		$output .= '<fieldset><legend id="'.sef($name).'-toggle">'.$this->icon['show_hide'].' <b>'.$name.'</b></legend>
			<div id="'.sef($name).'-slider">';
		$this->open_slider = TRUE;

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### CLOSE SLIDER ##########################################################
	function closeSlider() {
		$output = '</div></fieldset>';


		$this->open_slider = FALSE;

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}


	#### VALIDATE ##############################################################
	function validate($id, $type = FALSE, $max = FALSE, $min = FALSE, $message = FALSE, $required = FALSE) {
		// Set some variables to be used by $this->input()
		$this->validation[$id]['id'] = TRUE;
		$this->validation[$id]['type'] = $type;
		$this->validation[$id]['message'] = $message;
		$this->validation[$id]['min'] = $min;
		$this->validation[$id]['max'] = $max;
		$this->validation[$id]['required'] = $required;
	}

	#### INPUT FIELDS ##########################################################
	function input($type, $name, $id, $value = NULL, $default = NULL, $size = 30, $max_length = FALSE) {
		$extra = NULL;

		// What type of size do we have, % or not?
		if (strstr($size, '%')) $extra .= ' style="width: '.$size.'"';
		else $extra .= ' size="'.$size.'"';

		// Has a max length been set?
		if($max_length) $extra .= ' maxlength="'.$max_length.'" ';

		// If a defult value has been set but not actual value passed we use the default value as the value
		if ($default && !$value) $value = $default;

		// If it's not a hidden input and it has a name we can build it
		if ($type != 'hidden' && $name) $name = $this->buildName($name, $id); else $name = NULL;

		// Check if this password field uses a password meter
		if ($type == 'password' && @$this->password_meter[$id] == TRUE) $extra .= ' onkeyup="password_strength(\''.$id.'\' , this.value)" ';

		// Check if this field uses JS validation
		$extra .= (isset($this->validation[$id]['id']) && $this->validation[$id]['id'] ? ' onkeyup="validate(\''.$id.'\', \''.$this->validation[$id]['type'].'\', \''.$this->validation[$id]['message'].'\',  \''.$this->validation[$id]['min'].'\', \''.$this->validation[$id]['max'].'\')" ' : NULL);
		$extra .= (isset($this->validation[$id]['required']) && $this->validation[$id]['required'] ? ' onblur="required(\''.$id.'\', \''.$this->validation[$id]['required'].'\')" ' : NULL);

		// If the id already looks like name[element] don't modify it, this is needed for file input too
		if (strstr($id, '[') || $type == 'file')
			$output = '<input type="'.$type.'" name="'.$id.'" id="'.$id.'" value="'.htmlentities($value).'"'.$extra.' />';
		// Else add the formname[id]
		else $output = '<input type="'.$type.'" name="'.$this->form_id.'['.$id.']" id="'.$id.'" value="'.htmlentities($value).'"'.$size.' '.$extra.' />';

		// If we are not in a group add the DL's and DD's
		if ($this->open_group) $output = $name.$output;
		else $output = '<dl>'.$name.'<dd>'.$output.'</dd></dl>';

		if ($this->return_item_output) return $output;
		$this->output .= $output;

	}

	#### TEXT ##################################################################
	function text($name, $id, $value = NULL, $default = NULL, $size = 30, $max_length = FALSE) {
		return $this->input('text', $name, $id, $value, $default, $size, $max_length);
	}

	#### PASSWORD ##############################################################
	function password($name, $id, $value = NULL, $default = NULL, $size = 30, $max_length = FALSE) {
		return $this->input('password', $name, $id, $value, $default, $size, $max_length);
	}

	#### PASSWORD METER ########################################################
	function passwordMeter($name, $id) {

		// Used by $this->input()
		$this->password_meter[$id] = TRUE;

		// Call our function to dump some CSS on our page
		$output = '<script type="text/javascript">password_strength(\''.$id.'\');</script>';

		if ($name) $name = $name.':';

		$output .= $this->openGroup();
		$output .= '<dd>';
		$output .= '<div style="float:left"><b>'.$name.'</b>&nbsp;&nbsp;</div>';
		$output .= '<div id="password_description_'.$id.'"></div><div id="password_strength_box"><div id="password_strength_'.$id.'" class="password_strength_0"></div></div>';
		$output .= '</dd>';
		$output .= $this->closeGroup();

		$output = '<dl>'.$output.'</dl>';

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### FILE ##################################################################
	function file($name, $id, $value = NULL, $default = NULL, $size = 30, $max_length = FALSE) {
		return $this->input('file', $name, $id, $value, $default, $size, $max_length);
	}

	#### HIDDEN ################################################################
	function hidden($id, $value = NULL) {
		return $this->input('hidden', $name = NULL, $id, $value);
	}

	#### CHECKBOX ##############################################################
	function checkbox($name, $id, $checked = NULL, $default = NULL) {

		$output = NULL;

		if (!$default && $checked == 1) $checked = ' checked="checked"';
 		else if ($default == 1) $checked = ' checked="checked"';
		else $checked = NULL;

		// Check if this field has a description
		if (isset($this->description[$id]))
			$description = '<br /><span>'.$this->description[$id].'</span>';

		if ($name && !$this->open_group) $name = $this->buildName($name, $id);

		// First we put a hidden field with a value of 0 so even if a checkbox is not
		// checked it's name and value will be returned - clever eh?
		$output .= '<input type="hidden" name="'.$this->form_id.'['.$id.']" value="0" />
			<input type="checkbox" name="'.$this->form_id.'['.$id.']" id="'.$id.'" value="1"'.$checked.' class="checkbox" />';

		// If we are not in a group add the DL's and DD's
		if ($this->open_group) $output = $name.$output;
		else $output = '<dl>'.$name.'<dd>'.$output.'</dd></dl>';

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### TEXTAREA ##############################################################
	function textarea($name, $id, $value = NULL, $width_percent = 100, $height_pixels = 100, $read_only = FALSE) {

		if($read_only) $read_only = 'readonly="readonly" ';

		if ($name) $name = $this->buildName($name, $id);
		$output = '<div style="width: 100%; _width: 95%;"><div><textarea name="'.$this->form_id.'['.$id.']" id="'.$id.'" rows="0" style="width:'.$width_percent.'%; height:'.$height_pixels.'px;" '.$read_only.'cols="">'.$value.'</textarea></div></div>';

		// If we are not in a group add the DL's and DD's
		if ($this->open_group) $output = $name.$output;
		else $output = '<dl>'.$name.'<dd>'.$output.'</dd></dl>';

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### TEXT EDITOR ###########################################################
	function texteditor($name, $id, $value = NULL, $width_percent = 100, $height_pixels = 100, $args = array()) {

		// $args array can be used for passing data to third party editors
		if ($this->config['text_editor'] == 'None')
			$output = $this->textarea($name, $id, $value, $width_percent, $height_pixels);
		// Inclue editor
		else if (file_exists('includes/misc/editor_'.$this->config['text_editor'].'.inc')) {
			require_once('includes/misc/editor_'.$this->config['text_editor'].'.inc');
		} else die('The editor editor_'.$config['text_editor'].'.inc could not be included');

		// Build the name
		if ($name) $name = $this->buildName($name, $id);

		// If we are not in a group add the DL's and DD's
		if ($this->open_group) $output = $name.$output;
		else $output = '<dl>'.$name.'<dd>'.$output.'</dd></dl>';

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### RADIO #################################################################
	function radio($name, $id, $options, $checked = NULL) {

		// Build the name
		if ($name) $name = $this->buildName($name, $id);

		// Build the options
		$output = NULL;
		if ($options) foreach($options as $value => $option) {
			$output .= $this->radioOption($id, $option, $value, $checked);
		}

		// If we are not in a group add the DL's and DD's
		if ($this->open_group) $output = $name.$output;
		else $output = '<dl>'.$name.'<dd>'.$output.'</dd></dl>';

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### RADIO OPTIONS #########################################################
	function radioOption($id, $option, $value, $checked = NULL) {
		if ($value == $checked) $checked = ' checked="checked"';
		else $checked = NULL;

		return '<input type="radio" class="radio" name="'.$this->form_id.'['.$id.']" id="'.$id.'" value="'.$value.'"'.$checked.' />'.$option.'&nbsp;&nbsp;';
	}

	#### SELECT ################################################################
	function select($name, $id, $options, $selected = NULL, $default = NULL, $size = 1, $width_percent = NULL) {
		// Decide what is the default
		if (!$selected && $default) $selected = $default;

		// Build the name
		if ($name) $name = $this->buildName($name, $id);

		// Add the options
		$return = NULL;
		if ($options) foreach($options as $value => $option) {
				$return .= $this->selectOption($option, $value, $selected);
		}

		// What type of size do we have, % or not?
		if ($width_percent) $width_percent = 'style="width: '.$width_percent.'%"';

		$output = '<select name="'.$this->form_id.'['.$id.']" id="'.$id.'" size="'.$size.'" '.$width_percent.'>'.$return.'</select>';

		// If we are not in a group add the DL's and DD's
		if ($this->open_group) $output = $name.$output;
		else $output = '<dl>'.$name.'<dd>'.$output.'</dd></dl>';

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### SELECT OPTIONS ########################################################
	function selectOption($name, $value, $selected) {

		// Type needs to be a string to avoid values like "2+" being TRUE for "2"
		settype($value, 'string');

		if ($value == $selected) $selected = ' selected="selected"';
		else if ($name == $selected) $selected = ' selected="selected"';
		else $selected = NULL;

		return '<option value="'.$value.'"'.$selected.'>'.$name.'</option>';
	}

	#### DATE SELECT ###########################################################
	function date($name, $datetime = FALSE, $display = 'hour minute day month year', $field_prefix = NULL) {

		// Ensure no offset is applied if a datetime is supplied
		if ($datetime) $datetime = datetime(NULL, $datetime, FALSE);

		if ($field_prefix) $field_prefix = $field_prefix.'_';

		// Hour
		$select_hour = '<select name="'.$this->form_id.'['.$field_prefix.'hour]">';
		for ($i = 0; $i <= 23; $i++) {
			$select_hour .= '<option';
			if($i == datetime('H', $datetime)) $select_hour .= ' selected="selected"';
			$select_hour .= '>'.$i.'</option>';
		}
		$select_hour .= '</select>';

		// Minute
		$select_minute = '<select name="'.$this->form_id.'['.$field_prefix.'minute]">';
		for ($i = 0; $i <= 59; $i++) {
			$select_minute .= '<option';
			if($i == datetime('i', $datetime)) $select_minute .= ' selected="selected"';
			$select_minute .= '>'.$i.'</option>';
		}
		$select_minute .= '</select>';

		// Day
		$select_day = '<select name="'.$this->form_id.'['.$field_prefix.'day]">';
		for ($i = 1; $i <= 31; $i++) {
			$select_day .= '<option';
			if($i == datetime('d', $datetime)) $select_day .= ' selected="selected"';
			$select_day .= '>'.$i.'</option>';
		}
		$select_day .= '</select>';

		// Month
		$select_month = '<select name="'.$this->form_id.'['.$field_prefix.'month]">';
		for ($i = 1; $i <= 12; $i++) {
			$select_month .= '<option';
			if($i == datetime('m', $datetime)) $select_month .= ' selected="selected"';
			$select_month .= '>'.$i.'</option>';
		}
		$select_month .= '</select>';

		// Year
		$select_year = '<input type="text" name="'.$this->form_id.'['.$field_prefix.'year]" class="text" size="4" maxlength="4" value="'.datetime('Y', $datetime).'" />';

		if ($name) $name = $this->buildName($name, strtolower($name));
		$output = NULL;
		if ($display && strstr($display, 'hour')) $output .= $select_hour.' ';
		if ($display && strstr($display, 'minute')) $output .= $select_minute.'&nbsp;&nbsp;&nbsp;';
		if ($display && strstr($display, 'day')) $output .= $select_day.' ';
		if ($display && strstr($display, 'month')) $output .= $select_month.' ';
		if ($display && strstr($display, 'year')) $output .= $select_year.' ';

		// If we are not in a group add the DL's and DD's
		if ($this->open_group) $output = $name.$output;
		else $output = '<dl>'.$name.'<dd>'.$output.'</dd></dl>';

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### ROLES #################################################################
	function roles($name, $id, $selected = NULL, $default = NULL, $names_only = NULL) {

		// Set up a variable for while iteration dection
		$looped = NULL;

		// Run query to get our role data
		$sql = "SELECT role_id, position, name FROM ".$this->db->table['roles']." ORDER BY position DESC";
		if (!$result = $this->db->query($sql, $this->db->connection['roles'])) sql_error(__FILE__, __LINE__);

		// Don't show "and above" for top level, ignore "and below" for anon and
		// ignore "and above" and "and below" for banned
		while ($row = $this->db->fetchArray($result)) {
			// Only make a role selectable if we have access to it
			if (allowed($row['role_id'].'+')) {
				if (!$names_only && $looped && $row['role_id'] != 2) $roles[$row['role_id'].'+'] = $row['name'].' and Above';
				if (!$names_only) $roles[$row['role_id']] = $row['name'].' Only';
				if (!$names_only && $row['role_id'] != 2 && $row['role_id'] != 1) $roles[$row['role_id'].'-'] = $row['name'].' and Below';
				// Return group id when only a listing of groups is needed
				if ($names_only) $roles[$row['role_id']] = $row['name'];
				$looped = TRUE;
			}
		}

		// Return the information after passing it through the select function
		return $this->select($name, $id, $roles, $selected, $default);
	}

	#### CAPTCHA ###############################################################
	function captcha($name = 'Capcha', $id = 'captcha', $width = 100, $height = 35, $length = 5) {

		// Delete old captcha images
		if ($dir = opendir('media/captcha/')) {
			while (FALSE != ($file = readdir($dir))) {
				if (strstr($file, '.jpg') && (time() - filemtime('media/captcha/'.$file)) > 300)
					unlink('media/captcha/'.$file);
			}
			closedir($dir);
		}

		// Set up the font, random file name and captcha code
		$font = 'includes/misc/monofont.ttf';
		$random_filename = md5(mt_rand(100000,999999));
		$this->load->helper('password');
		$captcha_code = password_generator($length);

		// Set font size to 75% of the image height
		$font_size = $height * 0.75;
		$image = imagecreate($width, $height) or die('Cannot initialize new GD image stream');

		// Set the colours
		$background_color = imagecolorallocate($image, 240, 240, 240);
		$text_color = imagecolorallocate($image, 0, 0, 0);
		$noise_color = imagecolorallocate($image, 150, 150, 150);

		// Generate random lines in background
		for($i=0; $i < ($width * $height) / 150; $i++)
			imageline($image, mt_rand(0,$width), mt_rand(0,$height), mt_rand(0,$width), mt_rand(0,$height), $noise_color);

		// Create textbox and add text
		$textbox = imagettfbbox($font_size, 0, $font, $captcha_code) or die('Error in imagettfbbox function');
		$x = ($width - $textbox[4])/2;
		$y = ($height - $textbox[5])/2;
		imagettftext($image, $font_size, 0, $x, $y, $text_color, $font, $captcha_code) or die('Error in imagettftext function');

		// Return captcha image and store code in session
		$outfile = $random_filename.'.jpg';
		imagejpeg($image, 'media/captcha/'.$outfile, 90);
		imagedestroy($image);
		$this->sessvar['captcha_code'] = $captcha_code;

		// What follows is one very ugly hack beacuse of the form output building process and return_item_output option
		// There has to be a better way of doing this but I'm tired so...
		$this->output .= '<dl>';
		if ($this->return_item_output) $output = '<dl>';
		$output = $this->label($name.'*');
		if ($this->return_item_output) $this->output .= '</dl>';
		$output .= '</dl>';

		$this->output .= '<dd><img src="media/captcha/'.$outfile.'" alt="Captcha Code" /><br /><b>Please enter the text above:</b><br />';
		if ($this->return_item_output) $output .= '<dd><img src="media/captcha/'.$outfile.'" alt="Captcha Code" /><br /><b>Please enter the text above:</b><br />';

		// Setting this to true prevents DD's and DL's
		$this->open_group = TRUE;
		$output .= $this->text(NULL, $id, NULL, NULL, $length + 2);'</dd>';
		$this->open_group = FALSE;

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### BUTTON ################################################################
	function button($type = 'submit', $value = 'submit', $alt = NULL, $group = FALSE, $id = 'submit', $extra = FALSE, $no_button_class = FALSE) {
		//if (!$no_button_class) $no_button_class = ' class="button"';
		$output = '<input type="'.$type.'" name="'.$this->form_id.'['.$id.']" id="'.$id.'" value="'.$value.'" alt="'.$alt.'" title="'.$alt.'" '.$extra.$no_button_class.' />';

		if (!$group && !$this->open_group) $output = '<dl><dd>'.$output.'</dd></dl>';

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### IMAGE ################################################################
	function imageButton($src, $value = 'submit', $alt = NULL, $group = FALSE, $id = 'submit', $extra = FALSE, $width = NULL, $height = NULL) {

		if ($width) $width = ' width="'.$width.'"';
		if ($height) $height = ' height="'.$height.'"';

		$output = '<input type="image" name="'.$this->form_id.'['.$id.']" src="'.$src.'" value="'.$value.'" border="0" alt="'.$alt.'" title="'.$alt.'" '.$width.$height.$extra.' class="image_button" />';

		if (!$group && !$this->open_group) $output = '<dl><dd>'.$output.'</dd></dl>';

		if ($this->return_item_output) return $output;
		$this->output .= $output;

	}

	#### OPEN GROUP ############################################################
	function openGroup($name = NULL, $drop = NULL, $center = FALSE) {

		// Setting this to true prevents DD's and DL's and forces form input to appear
		// below the group name making it possible to use the full width of the form
		$this->drop = ($drop ? TRUE : FALSE);

		// Setting this to true prevents DD's and DL's
		$this->open_group = TRUE;

		$output = NULL;
		if ($name) $name = $this->buildName($name, $name);
		if ($this->drop) $output .= '<dl>'.$name.'</dl><dl><div style="clear: both"></div>';
		else $output .= '<dl>'.$name.'<dd><div class="group">';

		if ($center) {
			$this->center = TRUE;
			$output .= '<center>';
		}

		if ($this->return_item_output) return $output;
		else $this->output .= $output;
	}

	#### CLOSE GROUP ###########################################################
	function closeGroup() {

		$output = NULL;
		if ($this->center) $output .= '</center>';
		if (!isset($this->drop)) { $output .= '</div>'.$this->extra.'</dd>'; echo '.'; }
		$output .= '</dl>';

		// Best set these back to false
		$this->open_group = FALSE;
		$this->drop = FALSE;
		$this->center = FALSE;

		if ($this->return_item_output) return $output;
		$this->output .= $output;

	}

	#### CLOSE #################################################################
	function close($hide_required_message = FALSE) {
		$output = '</form>';
		if ($this->required == TRUE && !$hide_required_message) $output .= '<br /><label><span class="required">*</span> Required field</label>';

		// Just a little note to developers
		if ($this->open_group == TRUE) $output .= '<br /><big>Hey, there is an unclosed group!</big>';
		if ($this->open_fieldset == TRUE) $output .= '<br /><big>Hey, there is an unclosed fieldset!</big>';
		if ($this->open_slider == TRUE) $output .= '<br /><big>Hey, there is an unclosed slider!</big>';

		if ($this->return_item_output) return $output;
		$this->output .= $output;
	}

	#### OUTPUT ################################################################
	function output() {
		return $this->output;
	}
}
?>